package com.jsy.common.util.m3u8;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * M3U8视频下载工具类
 * 支持从M3U8索引文件下载TS片段并合并为完整视频
 */
public class M3U8Downloader {
    private String m3u8Url;               // M3U8索引文件URL
    private String saveDir;               // 保存目录
    private int threadCount = 5;          // 下载线程数
    private boolean isLive = false;       // 是否为直播流
    private long retryDelay = 1000;       // 重试延迟(毫秒)
    private int maxRetries = 3;           // 最大重试次数
    private boolean overwrite = false;    // 是否覆盖已存在文件
    private DownloadListener listener;    // 下载监听器

    // 用于匹配M3U8中的TS片段URL
    private static final Pattern TS_URL_PATTERN = Pattern.compile("^[^#].*\\.(ts|mp4|m4s)$");
    // 用于匹配M3U8中的EXT-X-STREAM-INF标签(多层M3U8)
    private static final Pattern STREAM_INF_PATTERN = Pattern.compile("#EXT-X-STREAM-INF:.*\\s(.*m3u8)");

    public M3U8Downloader(String m3u8Url, String saveDir) {
        this.m3u8Url = m3u8Url;
        this.saveDir = saveDir;
    }

    // 设置下载线程数
    public M3U8Downloader setThreadCount(int threadCount) {
        this.threadCount = threadCount;
        return this;
    }

    // 设置是否为直播流
    public M3U8Downloader setLive(boolean live) {
        isLive = live;
        return this;
    }

    // 设置重试策略
    public M3U8Downloader setRetryPolicy(long retryDelay, int maxRetries) {
        this.retryDelay = retryDelay;
        this.maxRetries = maxRetries;
        return this;
    }

    // 设置是否覆盖已存在文件
    public M3U8Downloader setOverwrite(boolean overwrite) {
        this.overwrite = overwrite;
        return this;
    }

    // 设置下载监听器
    public M3U8Downloader setListener(DownloadListener listener) {
        this.listener = listener;
        return this;
    }

    // 开始下载
    public void download() throws IOException, InterruptedException {
        // 创建保存目录
        Path savePath = Paths.get(saveDir);
        if (!Files.exists(savePath)) {
            Files.createDirectories(savePath);
        }

        // 解析M3U8文件获取TS片段列表
        List<String> tsUrls = parseM3U8(m3u8Url);
        if (tsUrls.isEmpty()) {
            throw new IOException("未找到TS片段URL");
        }

        // 检查是否需要断点续传
        List<String> downloadedTsFiles = getDownloadedTsFiles();
        List<String> remainingTsUrls = new ArrayList<>();
        
        for (String tsUrl : tsUrls) {
            String fileName = getFileNameFromUrl(tsUrl);
            if (!downloadedTsFiles.contains(fileName) || overwrite) {
                remainingTsUrls.add(tsUrl);
            }
        }

        if (remainingTsUrls.isEmpty()) {
            if (listener != null) {
                listener.onDownloadComplete();
            }
            return;
        }

        // 多线程下载TS片段
        downloadTsFiles(remainingTsUrls);

        // 合并TS文件
        if (!isLive) {
            mergeTsFiles(tsUrls);
        }

        if (listener != null) {
            listener.onDownloadComplete();
        }
    }

    // 解析M3U8文件获取TS片段URL列表
    private List<String> parseM3U8(String m3u8Url) throws IOException {
        List<String> tsUrls = new ArrayList<>();
        URL url = new URL(m3u8Url);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestProperty("User-Agent", "Mozilla/5.0");
        
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
            String line;
            String baseUrl = getBaseUrl(m3u8Url);
            
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                
                // 处理多层M3U8
                Matcher streamMatcher = STREAM_INF_PATTERN.matcher(line);
                if (streamMatcher.find()) {
                    String subM3U8Url = resolveUrl(baseUrl, streamMatcher.group(1));
                    tsUrls.addAll(parseM3U8(subM3U8Url));
                    continue;
                }
                
                // 处理TS片段URL
                Matcher tsMatcher = TS_URL_PATTERN.matcher(line);
                if (tsMatcher.matches()) {
                    tsUrls.add(resolveUrl(baseUrl, line));
                }
            }
        }
        
        return tsUrls;
    }

    // 获取URL的基础路径
    private String getBaseUrl(String url) {
        int lastSlashIndex = url.lastIndexOf('/');
        if (lastSlashIndex != -1) {
            return url.substring(0, lastSlashIndex + 1);
        }
        return "";
    }

    // 解析相对URL为绝对URL
    private String resolveUrl(String baseUrl, String relativeUrl) {
        if (relativeUrl.startsWith("http")) {
            return relativeUrl;
        }
        return baseUrl + relativeUrl;
    }

    // 从URL获取文件名
    private static String getFileNameFromUrl(String url) {
        int lastSlashIndex = url.lastIndexOf('/');
        int lastQuestionIndex = url.lastIndexOf('?');
        
        if (lastSlashIndex != -1) {
            String fileName = url.substring(lastSlashIndex + 1);
            if (lastQuestionIndex != -1 && lastQuestionIndex > lastSlashIndex) {
                fileName = fileName.substring(0, lastQuestionIndex - lastSlashIndex - 1);
            }
            return fileName;
        }
        
        return url;
    }

    // 获取已下载的TS文件列表
    private List<String> getDownloadedTsFiles() throws IOException {
        List<String> downloadedFiles = new ArrayList<>();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(saveDir), "*.ts")) {
            for (Path path : stream) {
                downloadedFiles.add(path.getFileName().toString());
            }
        }
        return downloadedFiles;
    }

    // 多线程下载TS文件
    private void downloadTsFiles(List<String> tsUrls) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        BlockingQueue<DownloadTask> taskQueue = new LinkedBlockingQueue<>(tsUrls.size());
        AtomicInteger totalTasks = new AtomicInteger(tsUrls.size());
        AtomicInteger completedTasks = new AtomicInteger(0);
        
        // 提交下载任务
        for (String tsUrl : tsUrls) {
            taskQueue.offer(new DownloadTask(tsUrl, saveDir, maxRetries, retryDelay, 
                () -> {
                    int completed = completedTasks.incrementAndGet();
                    if (listener != null) {
                        listener.onProgress(completed, totalTasks.get());
                    }
                }));
        }
        
        // 创建工作线程
        for (int i = 0; i < threadCount; i++) {
            executor.submit(() -> {
                try {
                    DownloadTask task;
                    while ((task = taskQueue.poll(1, TimeUnit.SECONDS)) != null) {
                        task.execute();
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.HOURS);
    }

    // 合并TS文件
    private void mergeTsFiles(List<String> tsUrls) throws IOException {
        String outputFilePath = saveDir + File.separator + "merged.mp4";
        File outputFile = new File(outputFilePath);
        
        if (outputFile.exists() && !overwrite) {
            System.out.println("合并文件已存在，跳过合并: " + outputFilePath);
            return;
        }
        
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             FileChannel outChannel = fos.getChannel()) {
            
            for (String tsUrl : tsUrls) {
                String fileName = getFileNameFromUrl(tsUrl);
                File tsFile = new File(saveDir + File.separator + fileName);
                
                if (tsFile.exists()) {
                    try (FileInputStream fis = new FileInputStream(tsFile);
                         FileChannel inChannel = fis.getChannel()) {
                        
                        inChannel.transferTo(0, inChannel.size(), outChannel);
                    }
                }
            }
        }
        
        System.out.println("TS文件合并完成: " + outputFilePath);
    }

    // 下载任务内部类
    private static class DownloadTask {
        private final String url;
        private final String saveDir;
        private final int maxRetries;
        private final long retryDelay;
        private final Runnable onComplete;

        public DownloadTask(String url, String saveDir, int maxRetries, long retryDelay, Runnable onComplete) {
            this.url = url;
            this.saveDir = saveDir;
            this.maxRetries = maxRetries;
            this.retryDelay = retryDelay;
            this.onComplete = onComplete;
        }

        public void execute() {
            String fileName = getFileNameFromUrl(url);
            File outputFile = new File(saveDir + File.separator + fileName);
            
            if (outputFile.exists()) {
                System.out.println("文件已存在，跳过下载: " + fileName);
                if (onComplete != null) {
                    onComplete.run();
                }
                return;
            }
            
            int retries = 0;
            while (retries < maxRetries) {
                try {
                    downloadFile(url, outputFile);
                    System.out.println("下载完成: " + fileName);
                    if (onComplete != null) {
                        onComplete.run();
                    }
                    return;
                } catch (Exception e) {
                    retries++;
                    System.err.println("下载失败 (" + retries + "/" + maxRetries + "): " + fileName + " - " + e.getMessage());
                    
                    if (retries < maxRetries) {
                        try {
                            Thread.sleep(retryDelay);
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                }
            }
            
            System.err.println("下载失败，达到最大重试次数: " + fileName);
        }

        private void downloadFile(String urlStr, File outputFile) throws IOException {
            URL url = new URL(urlStr);
            try (ReadableByteChannel rbc = Channels.newChannel(url.openStream());
                 FileOutputStream fos = new FileOutputStream(outputFile);
                 FileChannel fc = fos.getChannel()) {
                
                fc.transferFrom(rbc, 0, Long.MAX_VALUE);
            }
        }
    }

    // 下载监听器接口
    public interface DownloadListener {
        void onProgress(int completed, int total);
        void onDownloadComplete();
    }

    // 示例用法
    public static void main(String[] args) {
//        String m3u8Url = "https://example.com/stream/index.m3u8";
        String m3u8Url = "https://v10.gggread.com/wjv10/202505/17/g1eT36kYLz82/video/index.m3u8";
        String saveDir = "./downloads";
        
        try {
            new M3U8Downloader(m3u8Url, saveDir)
                .setThreadCount(10)
                .setRetryPolicy(2000, 5)
                .setOverwrite(false)
                .setListener(new DownloadListener() {
                    @Override
                    public void onProgress(int completed, int total) {
                        System.out.printf("下载进度: %.2f%% (%d/%d)\n", 
                            (completed * 100.0 / total), completed, total);
                    }
                    
                    @Override
                    public void onDownloadComplete() {
                        System.out.println("全部下载完成!");
                    }
                })
                .download();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}